#!/usr/bin/env python3
import logging
import json
import os
import signal
import sys
import urllib
import urllib.request
# import urlparse

from multiprocessing import Process

import oauth2
from oauth2 import Provider
from oauth2.error import UserNotAuthenticated
from oauth2.grant import AuthorizationCodeGrant
from oauth2.tokengenerator import Uuid4
from oauth2.store.memory import ClientStore, TokenStore
from oauth2.web import AuthorizationCodeGrantSiteAdapter

from wsgiref.simple_server import make_server, WSGIRequestHandler


class ClientRequestHandler(WSGIRequestHandler):
    """
    Request handler that enables formatting of the log messages on the console.
    This handler is used by the client application. 
    """
    def address_string(self):
        return "client app".encode(encoding="UTF8")


class OAuthRequestHandler(WSGIRequestHandler):
    """
    Request handler that enables formatting of the log messages on the console.
    This handler is used by the python-oauth2 application.
    """
    def address_string(self):
        return "python-oauth2 handler".encode(encoding="UTF8")


class ClientApplication(object):
    """
    Very basic application that simulates calls to the API of the 
    python-oauth2 app.
    """

    callback_url = "http://192.168.200.222:8081/callback"
    client_id = "car"
    client_secret = "xyz"
    api_server_url = "http://192.168.200.222:8080"

    def __init__(self):
        self.access_token = None        
        self.auth_token = None
        self.token_type = ""

    def __call__(self, env, start_response):
        if env["PATH_INFO"] == "/app":
            status, body, headers = self._serve_application(env)

        elif env["PATH_INFO"] == "/callback":
            status, body, headers = self._read_auth_token(env)
        else:
            status = "301 Moved"
            body = ""
            headers = {"Location": "/app"}

        start_response(status, 
            [(header, val) for header, val in headers.items()])
        print("AAAAAAAAAAAAAAAA")
        return body

    def _request_access_token(self):
        print("Reequesting access token...")

        post_params = {
            "client_id": self.client_id,
            "client_secret": self.client_secret,
            "code": self.auth_token,
            "grant_type": "authorization_code",
            "redirect_uri": self.callback_url
        }
        
        token_endpoint = self.api_server_url + "/token"

        result = urllib.request.urlopen(token_endpoint, 
                                urllib.parse.urlencode(post_params).encode(encoding="UTF8"))

        content = ""
        for line in result:
            content += line.decode(encoding="UTF8")

        result = json.loads(content)
        self.access_token = result["access_token"]
        self.token_type = result["token_type"]

        confirmation = "Received access token '%s' of type '%s'" \
                            %(self.access_token, self.token_type)
        print(confirmation)
        return "302 Found", "", {"Location": "/app"}

    def _read_auth_token(self, env):
        print("Receving authorization token...")

        query_params = urllib.parse.parse_qs(env["QUERY_STRING"])

        if "error" in query_params:
            location = "/app?error" + query_params["error"][0]
            return "302 Found", "", {"Location":location}

        self.auth_token = query_params["code"][0]
        print("Received tenporary authorization token '%s'" %(self.auth_token))
        return "302 Found", "", {"Location": "/app"}

    def _request_auth_token(self):
        print("Requesting authorization token...")

        auth_endpoint = self.api_server_url + "/authorize"
        query = urllib.parse.urlencode({
            "client_id": self.client_id,
            "redirect_uri": self.callback_url,
            "response_type": "code"
            })

        location = "%s?%s" %(auth_endpoint, query)
        return "302 Found", "", {"Location":location}

    def _serve_application(self, env):
        query_params = urllib.parse.parse_qs(env["QUERY_STRING"])

        if ("error" in query_params \
            and query_params["error"][0] == "access_denied"):
            return "200 OK", "User has denied access", {}

        if self.access_token is None:
            if self.auth_token is None:
                return self._request_auth_token()
            else:
                return self._request_access_token()
        else:
            confirmation = "Currnet access token '%s' of type '%s'"\
                        %(self.access_token, self.token_type)
            print(confirmation)
            return "200 OK", str(confirmation), {}

def get_host_ip(ifname):
    import socket
    import fcntl
    import struct

    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    info_b = fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', ifname[:15].encode("UTF8")))
    return socket.inet_ntoa(info_b[20:24])       

def run_app_server():
    app = ClientApplication()

    try:
        httpd = make_server("", 8081, app, handler_class=ClientRequestHandler)
        # httpd = make_server("", 8081, app)
        print("Starting Client app on 'http://localhost:8081/...'")
        httpd.serve_forever()
    except KeyboardInterrupt:
        httpd.server_close()

def main():
    app_server = Process(target=run_app_server)
    app_server.start()
    print("Access http://:8081/app in your browser")

    def sigint_handler(signal, frame):
        print("Terminating app_server...")
        app_server.terminate()
        app_server.join()

    signal.signal(signal.SIGINT, sigint_handler)

if __name__ == '__main__':
    main()